/*
 * FontChooser.java
 *
 * Created on 2011/06/22, 12:51:30
 */

package com.mrogrape.notepad.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.LayoutStyle;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.mrogrape.notepad.common.ViewUtils;
import com.mrogrape.system.Environment;
import com.mrogrape.util.StringUtils;



/**
 * FontChooser是一个方便的字体选择器，同时它还支持颜色的选取。
 * 
 * <p>
 * 以下代码演示了使用FontChooser的简单方法，更多的方法和功能请查看方法注释。
 * <pre><code>
 * //实例化字体选择器。
 * FontChooser chooser = new FontChooser();
 * //设置属性(这里只演示了标题属性)。
 * chooser.setTitle("Choose Font");
 * //显示对话框并接收返回值
 * int re = chooser.showChooser(parent);
 * //判断返回值以确定用户是否点击了“确定”。
 * if(re == FontChooser.APPROVE_OPTION){
 * Font selectedFont = chooser.getSelectedFont();
 * //使用选择的字体
 * //..............	
 * }
 * </code></pre>
 * @author Ice Spring
 */
public class FontChooser extends JPanel{

    /** SerialVersionUID*/
    private static final long serialVersionUID = 1L;

    /** 用以储存字体大小 */
    private static class FontSize{

	public FontSize(String size,String name){
	    this.size = size;
	    this.name = name;
	}

	@Override
	public String toString() {
	    return this.name;
	}


	@Override
	public boolean equals(Object obj) {
	    if(obj == null){
		return false;
	    }

	    if(!(obj instanceof FontSize)){
		return false;
	    }

	    FontSize fontSize = (FontSize) obj;
	    if(this.name.equals(fontSize.name)&&this.size.equals(fontSize.size)){
		return true;
	    }

	    return false;

	}


	String name;
	String size;
    }

    /** 用以实现可预览的字体列表而重写的渲染器 */
    private static class FontListCellRenderer 
    extends JLabel implements ListCellRenderer {

	/** SerialVersionUID */
	private static final long serialVersionUID = 1L;

	@Override
	public Component getListCellRendererComponent(JList list, Object value,
		int index, boolean isSelected, boolean cellHasFocus) {
	    String familyName = value.toString();
	    this.setText(familyName);
	    this.setFont(ViewUtils.createNewFont(familyName,Font.PLAIN,list.getFont().getSize()));
	    if (isSelected) {
		this.setBackground(list.getSelectionBackground());
		this.setForeground(list.getSelectionForeground());
	    } else {
		this.setBackground(list.getBackground());
		this.setForeground(list.getForeground());
	    }
	    this.setEnabled(list.isEnabled());
	    this.setOpaque(true);

	    return this;
	}

    }


    private class AutoCompleteHandler implements DocumentListener{


	public AutoCompleteHandler(JList list, JTextField textInput) {

	    this.list = list;
	    this.textInput = textInput;


	}

	@Override
	public void changedUpdate(DocumentEvent e) {

	    String text = this.textInput.getText();
	    if(StringUtils.isNotEmptyAfterTrim(text)){

		ListModel model = this.list.getModel();
		for(int i = 0; i < model.getSize(); i++){
		    String item = model.getElementAt(i).toString().toLowerCase();
		    if(item.startsWith(text.toLowerCase())){
			this.list.setSelectedValue( model.getElementAt(i), true);
			return;
		    }
		}
	    }

	}

	@Override
	public void insertUpdate(DocumentEvent e) {
	    this.changedUpdate(null);
	}

	@Override
	public void removeUpdate(DocumentEvent e) {
	    this.changedUpdate(null);
	}

	private JList list;
	private JTextField textInput;
    }

    /**
     * 表示用户肯定了自己的操作。
     */
    public static final int APPROVE_OPTION = 1;

    /**
     * 表示用于取消了自己的操作。
     */
    public static final int CANCEL_OPTION = 2;

    /**
     * 获取系统支持的全部可用字体家族的名称。
     * 
     * @return 系统支持的全部可用字体家族的名称。
     */
    public static final String[] getAvailableFontFamilyNames(){

	if(FontChooser.AVAILABLE_FONT_FAMILY_NAMES == null){
	    FontChooser.AVAILABLE_FONT_FAMILY_NAMES = Environment.availableFontFamilyNames();
	}

	return FontChooser.AVAILABLE_FONT_FAMILY_NAMES;
    }

    private static String[] AVAILABLE_FONT_FAMILY_NAMES = null;

    //FontChooser 初始化值
    private static final Font INIT_FONT = new JLabel().getFont();
    private static final Color INIT_COLOR = Color.BLACK;

    /**
     * 构造默认的FontChooser.
     */
    public FontChooser(){
	this(null,null);
    }

    /**
     * 构造FontChooser并指定初始化时选择的字体。
     * 
     * @param initFont 初始化字体。-可以为null；
     */
    public FontChooser(Font initFont){
	this(initFont,null);
    }

    /**
     * 构造FontChooser并指定初始化时选择的颜色。
     * 
     * <p>
     * <b>注意：</b>如果指定了颜色，颜色选择器将变为默认可见。如果不想显示颜色选择器。
     * 请使用<code>chooser.setShowColorChooser(false);</code>
     * 
     * @param initColor 初始化颜色。-可以为null;
     */
    public FontChooser(Color initColor){
	this(null,initColor);
    }

    /**
     * 构造FontChooser并指定初始化时选择的字体和颜色。
     * 
     * <p>
     * <b>注意：</b>如果指定了颜色，颜色选择器将变为默认可见。如果不想显示颜色选择器。
     * 请使用<code>chooser.setShowColorChooser(false);</code>
     * 
     * @param initFont 初始化字体。-可以为null;
     * @param initColor 初始化颜色。-可以为null;
     */
    public FontChooser(Font initFont,Color initColor){
	this.selectedFont = initFont;
	this.selectedColor = initColor;

	this.launch();
    }

    /**
     * 弹出颜色选择对话框并指定其父组件。
     * 
     * @param parent
     *            父组件，可以为null，详见{@link #showChooser(Component, String)}
     * @return 表示用户选择的常量，详见{@link #showChooser(Component, String)}
     */
    public int showChooser(Component parent){
	return this.showChooser(parent, null);
    }

    /**
     * 弹出颜色选择对话框并指定其父组件和标题。
     * 
     * <p>
     * <b>注意</b>，如果从未指定过Approve按显示的文本，则他们则由平台决定。
     * 
     * @param parent
     *            父组件，可以为null，为null时对话框将显示在屏幕中央并处于无模式 状态。
     * @param title
     *            对话框的标题，可以为空。
     *            
     * @return 返回值是以下两者之一
     *         <ul>
     *         <li>FontChooser.APPROVE_OPTION :表示用户点击了“approve”按钮。
     *         <li>FontChooser.CANCEL_OPTION :表示用户关闭了对话框或选择了Cancel按钮。
     *         </ul>
     * @see #setApproveButtonText(String)
     * @see #setCancelButtonText(String)
     */
    public int showChooser(Component parent,String title){

	if(title!=null){
	    this.setTitle(title);
	}

	this.dialog = this.createDialog(parent);
	this.dialog.addWindowListener(new WindowAdapter(){

	    @Override
	    public void windowClosing(WindowEvent e) {
		FontChooser.this.selectedOption = FontChooser.CANCEL_OPTION;
		FontChooser.this.dialog.setVisible(false);
	    }

	});
	this.fontListView.requestFocus();
	this.dialog.setVisible(true);
	this.dialog.removeAll();
	this.dialog.dispose();
	this.dialog = null;
	return this.selectedOption;


    }

    /**
     * 获取"approve"按钮显示的文本。
     * 
     * @return "approve"按钮显示的文本。
     */
    public String getApproveButtonText(){
	return this.approveBt.getText();
    }

    /**
     * 获取预览面板中显示的文字。
     * 
     * @return 预览面板中显示的文字。
     */
    public String getPreviewText(){
	return this.previewTextArea.getText();
    }

    /**
     * 获取"Cancel"按钮显示的文本。
     * 
     * @return "Cancel"按钮显示的文本。
     */
    public String getCancelButtonText(){
	return this.cancelBt.getText();
    }

    /**
     * 获取重置按钮上显示的文本。
     * 
     * @return 重置按钮上显示的文本。
     */
    public String getResetButtonText(){
	return this.resetBt.getText();
    }

    /**
     * 获取用户选择的字体。
     * 
     * <p>
     * 如果用户点击了Cancel或关闭了对话框，则这个值返回的是上一个有效的设定（通过
     * {@link #setSelectedFont(Font)}或构造方法或点击approve按钮）。如果这个值不存在，
     * 则返回空 
     * 
     * @return 选择的字体。
     * @see #setSelectedFont(Font)
     */
    public Font getSelectedFont(){
	return this.selectedFont;
    }

    /**
     * 获取用户选择的颜色。
     * 
     * <p>
     * 如果用户点击了Cancel或关闭了对话框，则这个值返回的是上一个有效的设定（通过
     * {@link #setSelectedColor(Color)}或构造方法或点击approve按钮）。如果这个值不存在，
     * 则返回空 
     * 
     * @return 选择的颜色。
     * @see #setSelectedColor(Color)
     */
    public Color getSelectedColor(){
	return this.selectedColor;
    }

    /**
     * 获取对话框的标题。
     * 
     * @return 对话框的标题。
     */
    public String getTitle(){
	return this.title;
    }

    /**
     * 查看是否显示颜色选择器。
     * 
     * @return true-显示；false-不显示
     */
    public boolean isShowColorChooser(){
	return this.isShowColorChooser;
    }

    /**
     * 查看是否显示重置按钮。
     * 
     * <p>
     * 点击重置按钮可以将当前选择状态返回对话框刚刚显示时。
     * 
     * @return true-显示；false-不显示
     */
    public boolean isShowResetButton(){
	return this.isShowResetButton;
    }

    /**
     * 设定approve按钮的文本。
     * 
     * @param text approve按钮的文本。
     */
    public void setApproveButtonText(String text){
	this.approveBt.setText(text);
    }

    /**
     * 设置Cancel按钮的文本。
     * 
     * @param text Cancel按钮的文本。
     */
    public void setCancelButtonText(String text){
	this.cancelBt.setText(text);
    }

    /**
     * 设置预览面板中预览的文字。
     * 
     * @param text 需要显示的文字。
     */
    public void setPreviewText(String text){
	this.previewTextArea.setText(text);
    }

    /**
     * 设置重置按钮上显示的文本。
     * 
     * @param text 需要在重置按钮上显示的文本。
     */
    public void setResetButtonText(String text){
	this.resetBt.setText(text);
    }

    /**
     * 设置默认选择的字体。
     * 
     * @param font 默认选择的字体
     */
    public void setSelectedFont(Font font){
	if(font == null){
	    return;
	}

	if(this.isAvailabelFontFamily(font)){

	    this.selectedFont = font;
	    this.fontListView.setSelectedValue(font.getFamily(), true);
	    this.sizeText.setText(String.valueOf(font.getSize()));
	    this.boldCk.setSelected(font.isBold());
	    this.italicCk.setSelected(font.isItalic());
	    this.sizeValueChanged(true, true);
	}
    }

    /**
     * 设置是否显示颜色选择器。
     * 
     * @param b 是否显示颜色选择器。true-显示；false-不显示
     */
    public void setShowColorChooser(boolean b){
	if(this.isShowColorChooser!=b){
	    this.isShowColorChooser = b;
	    if(b){
		this.colorChooserPanel.add(this.colorLb);
		this.colorChooserPanel.add(this.colorChooserLb);
	    }else{
		this.colorChooserPanel.remove(this.colorLb);
		this.colorChooserPanel.remove(this.colorChooserLb);
	    }
	}
    }

    /**
     * 设置是否显示重置按钮。
     * 
     * @param b 是否显示重置按钮。true-显示；false-不显示
     */
    public void setShowResetButton(boolean b){
	if(this.isShowResetButton != b){
	    if(this.isShowResetButton){
		this.buttonPanel.remove(this.resetBt);
	    }else{
		this.buttonPanel.add(this.resetBt);
	    }

	    this.isShowResetButton = b;
	}
    }

    /**
     * 设置默认选择的颜色。
     * 
     * @param color 默认选择的颜色。
     */
    public void setSelectedColor(Color color){
	if(color==null){
	    return;
	}
	this.selectedColor = color;
	this.colorChanged(color);
    }

    /**
     * 设置对话框的标题。
     * 
     * @param title 需要设置的标题。
     */
    public void setTitle(String title){
	this.title = title;
    }

    private void launch(){
	this.prepareValues();
	this.initComponents();
    }

    /** 初始化数据 */
    private void prepareValues(){
	//init preview font
	if(this.selectedFont == null){
	    this.previewFont = FontChooser.INIT_FONT;
	}else{
	    this.previewFont = this.selectedFont;
	}

	//init preview color
	if(this.selectedColor == null){
	    this.isShowColorChooser = false;
	    this.previewColor = FontChooser.INIT_COLOR;
	}else{
	    this.isShowColorChooser = true;
	    this.previewColor = this.selectedColor;
	}

	//init font family map
	for(String font : FontChooser.getAvailableFontFamilyNames()){
	    this.availableFontFamilyNameMap.put(font.toLowerCase().trim(), font);
	}

	//init available font size list
	for(FontSize size : this.preparedFontSizeArray){
	    this.preparedFontSizeMap.put(size.name, size);
	    this.preparedFontSizeList.add(size);
	}
	if(Locale.getDefault().getCountry().equals(Locale.CHINA.getCountry())){
	    for(FontSize size : this.chineseFontSizeArray){
		this.preparedFontSizeMap.put(size.name, size);
		this.preparedFontSizeList.add(size);
	    }
	}


	this.lastAvailableFontSize = this.createFontSize(this.previewFont);
	this.lastAvailableFontFamilyName = this.previewFont.getFamily();
    }

    private void initComponents() {

	this.chooserPanel = new JPanel();
	this.fontPanel = new JPanel();
	this.fontText = new JTextField();
	this.fontListScrollPane = new JScrollPane();
	this.fontListView = new JList();
	this.sizePanel = new JPanel();
	this.sizeText = new JTextField();
	this.sizeListScrollPane = new JScrollPane();
	this.sizeListView = new JList();
	this.stylePanel = new JPanel();
	this.styleChooserPanel = new JPanel();
	this.boldCk = new JCheckBox();
	this.italicCk = new JCheckBox();
	this.colorChooserPanel = new JPanel();
	this.colorLb = new JLabel();
	this.colorChooserLb = new JLabel();
	this.previewPanel = new JPanel();
	this.previewTextArea = new JTextArea();
	this.buttonPanel = new JPanel();
	this.cancelBt = new JButton();
	this.approveBt = new JButton();
	this.resetBt = new JButton();

	this.setMinimumSize(new Dimension(430, 380));

	this.fontPanel.setBorder(BorderFactory.createTitledBorder(FontChooser.label_font_panel_title));

	this.fontText.setToolTipText(FontChooser.tooltip_font_text);
	this.fontText.setText(this.previewFont.getName());
	this.fontText.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.fontTextFieldActionPerformed(evt);
	    }
	});
	this.fontText.addFocusListener(new FocusAdapter() {
	    @Override
	    public void focusLost(FocusEvent evt) {
		FontChooser.this.fontTextFieldFocusLost(evt);
	    }
	});
	this.fontText.getDocument().addDocumentListener(new AutoCompleteHandler(this.fontListView, this.fontText));


	this.fontListView.setToolTipText(FontChooser.tooltip_font_list);
	this.fontListView.setListData(FontChooser.AVAILABLE_FONT_FAMILY_NAMES);
	this.fontListView.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	this.fontListView.setCellRenderer(new FontListCellRenderer());
	this.fontListScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
	this.fontListScrollPane.setViewportView(this.fontListView);
	this.fontListView.setSelectedValue(this.previewFont.getName(), true);
	this.fontListView.setAutoscrolls(true);
	this.fontListView.addListSelectionListener(new ListSelectionListener(){
	    @Override
	    public void valueChanged(ListSelectionEvent e) {
		FontChooser.this.fontListViewValueChanged(e);
	    }
	});

	GroupLayout fontPanelLayout = new GroupLayout(this.fontPanel);
	this.fontPanel.setLayout(fontPanelLayout);
	fontPanelLayout.setHorizontalGroup(
		fontPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addComponent(this.fontText, GroupLayout.DEFAULT_SIZE, 158, Short.MAX_VALUE)
		.addComponent(this.fontListScrollPane, GroupLayout.DEFAULT_SIZE, 158, Short.MAX_VALUE)
	);
	fontPanelLayout.setVerticalGroup(
		fontPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(fontPanelLayout.createSequentialGroup()
			.addComponent(this.fontText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addComponent(this.fontListScrollPane, GroupLayout.DEFAULT_SIZE, 129, Short.MAX_VALUE))
	);

	this.sizePanel.setBorder(BorderFactory.createTitledBorder(FontChooser.label_size_panel_title));

	this.sizeText.setToolTipText(FontChooser.tooltip_size_text);
	this.sizeText.setText(String.valueOf(this.previewFont.getSize()));
	this.sizeText.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.sizeTextActionPerformed(evt);
	    }
	});
	this.sizeText.addFocusListener(new FocusAdapter() {
	    @Override
	    public void focusLost(FocusEvent evt) {
		FontChooser.this.sizeTextFocusLost(evt);
	    }
	});

	this.sizeListView.setToolTipText(FontChooser.tooltip_size_list);
	this.sizeListView.setAutoscrolls(true);
	this.sizeListView.setListData(this.preparedFontSizeList.toArray());
	this.sizeListView.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	this.sizeListScrollPane.setViewportView(this.sizeListView);
	this.sizeListView.setSelectedValue(this.createFontSize(this.previewFont), false);
	this.sizeListView.addListSelectionListener(new ListSelectionListener(){
	    @Override
	    public void valueChanged(ListSelectionEvent e) {
		FontChooser.this.sizeListViewValueChanged(e);
	    }	
	});

	GroupLayout sizePanelLayout = new GroupLayout(this.sizePanel);
	this.sizePanel.setLayout(sizePanelLayout);
	sizePanelLayout.setHorizontalGroup(
		sizePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addComponent(this.sizeText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
		.addComponent(this.sizeListScrollPane, GroupLayout.PREFERRED_SIZE, 63, GroupLayout.PREFERRED_SIZE)
	);

	sizePanelLayout.linkSize(SwingConstants.HORIZONTAL, new Component[] {this.sizeListScrollPane, this.sizeText});

	sizePanelLayout.setVerticalGroup(
		sizePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(sizePanelLayout.createSequentialGroup()
			.addComponent(this.sizeText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addComponent(this.sizeListScrollPane, GroupLayout.DEFAULT_SIZE, 129, Short.MAX_VALUE))
	);

	this.stylePanel.setBorder(BorderFactory.createTitledBorder(FontChooser.label_style_panel_title));

	this.styleChooserPanel.setLayout(new BoxLayout(this.styleChooserPanel, BoxLayout.Y_AXIS));

	this.boldCk.setText(FontChooser.label_bold_checkbox);
	this.boldCk.setSelected(this.previewFont.isBold());
	this.boldCk.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.boldCkActionPerformed(evt);
	    }
	});
	this.styleChooserPanel.add(this.boldCk);

	this.italicCk.setText(FontChooser.label_italic_checkbox);
	this.italicCk.setSelected(this.previewFont.isItalic());
	this.italicCk.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.italicCkActionPerformed(evt);
	    }
	});
	this.styleChooserPanel.add(this.italicCk);

	this.colorChooserPanel.setLayout(new FlowLayout(FlowLayout.LEFT));

	this.colorLb.setText(FontChooser.label_color_chooser);


	this.colorChooserLb.setCursor(new Cursor(Cursor.HAND_CURSOR));
	this.colorChooserLb.setToolTipText(FontChooser.tooltip_color_chooser);
	this.colorChanged(this.previewColor);
	this.colorChooserLb.addMouseListener(new MouseAdapter() {
	    @Override
	    public void mouseClicked(MouseEvent evt) {
		FontChooser.this.colorChooserLbMouseClicked(evt);
	    }
	});

	if(this.isShowColorChooser){
	    this.colorChooserPanel.add(this.colorLb);
	    this.colorChooserPanel.add(this.colorChooserLb);
	}

	GroupLayout stylePanelLayout = new GroupLayout(this.stylePanel);
	this.stylePanel.setLayout(stylePanelLayout);
	stylePanelLayout.setHorizontalGroup(
		stylePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addComponent(this.colorChooserPanel, GroupLayout.DEFAULT_SIZE, 89, Short.MAX_VALUE)
		.addComponent(this.styleChooserPanel, GroupLayout.DEFAULT_SIZE, 89, Short.MAX_VALUE)
	);
	stylePanelLayout.setVerticalGroup(
		stylePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(GroupLayout.Alignment.TRAILING, stylePanelLayout.createSequentialGroup()
			.addComponent(this.styleChooserPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
			.addComponent(this.colorChooserPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addContainerGap(77, Short.MAX_VALUE))
	);

	GroupLayout chooserPanelLayout = new GroupLayout(this.chooserPanel);
	this.chooserPanel.setLayout(chooserPanelLayout);
	chooserPanelLayout.setHorizontalGroup(
		chooserPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(chooserPanelLayout.createSequentialGroup()
			.addContainerGap()
			.addComponent(this.fontPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
			.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
			.addComponent(this.sizePanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
			.addComponent(this.stylePanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addContainerGap())
	);
	chooserPanelLayout.setVerticalGroup(
		chooserPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(GroupLayout.Alignment.TRAILING, chooserPanelLayout.createSequentialGroup()
			.addContainerGap()
			.addGroup(chooserPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING)
				.addComponent(this.stylePanel, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
				.addComponent(this.sizePanel, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
				.addComponent(this.fontPanel, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
				.addContainerGap())
	);

	this.previewPanel.setBorder(BorderFactory.createTitledBorder(FontChooser.label_preview_panel_title));
	this.previewPanel.setLayout(new BorderLayout());

	this.previewTextArea.setBackground(UIManager.getDefaults().getColor("Panel.background"));
	this.previewTextArea.setText(this.previewText);
	this.previewTextArea.setToolTipText(FontChooser.tooltip_preview_text);
	this.previewTextArea.setForeground(this.previewColor);
	this.previewTextArea.setEditable(false);
	this.previewTextArea.addMouseListener(new MouseAdapter(){
	    @Override
	    public void mouseClicked(MouseEvent e) {
		FontChooser.this.previewTextMouseClicked(e);
	    }
	});
	JScrollPane sp = new JScrollPane(this.previewTextArea);
	sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
	sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
	this.previewPanel.add(sp, BorderLayout.CENTER);

	this.cancelBt.setText(FontChooser.label_cancel_button);
	this.cancelBt.setToolTipText(FontChooser.tooltip_cancel_button);
	this.cancelBt.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.cancelBtActionPerformed(evt);
	    }
	});

	this.approveBt.setText(FontChooser.label_approve_button);
	this.approveBt.setToolTipText(FontChooser.tooltip_approve_button);
	this.approveBt.addActionListener(new ActionListener() {
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.approveBtActionPerformed(evt);
	    }
	});

	this.resetBt.setText(FontChooser.label_reset_button);
	this.resetBt.setToolTipText(FontChooser.tooltip_reset_button);
	this.resetBt.addActionListener(new ActionListener(){
	    @Override
	    public void actionPerformed(ActionEvent evt) {
		FontChooser.this.resetBtActionPerformed(evt);
	    }
	});


	this.buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
	this.buttonPanel.add(this.approveBt);
	this.buttonPanel.add(this.cancelBt);

	if(this.isShowResetButton){
	    this.buttonPanel.add(this.resetBt);
	}


	GroupLayout layout = new GroupLayout(this);
	this.setLayout(layout);
	layout.setHorizontalGroup(
		layout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addComponent(this.chooserPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
		.addGroup(layout.createSequentialGroup()
			.addContainerGap()
			.addComponent(this.previewPanel, GroupLayout.DEFAULT_SIZE, 372, Short.MAX_VALUE)
			.addContainerGap())
			.addGroup(layout.createSequentialGroup()
				.addContainerGap()
				.addComponent(this.buttonPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
				.addContainerGap())
	);
	layout.setVerticalGroup(
		layout.createParallelGroup(GroupLayout.Alignment.LEADING)
		.addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
			.addComponent(this.chooserPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
			.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
			.addComponent(this.previewPanel, GroupLayout.PREFERRED_SIZE, 91, GroupLayout.PREFERRED_SIZE)
			.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
			.addComponent(this.buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
			.addContainerGap())
	);

    }


    private void colorChooserLbMouseClicked(MouseEvent evt) {
	Color newColor = JColorChooser.showDialog(this,
		FontChooser.label_color_chooser_title, this.previewColor);
	if(newColor!=null){
	    this.colorChanged(newColor);
	}
    }

    private void boldCkActionPerformed(ActionEvent evt) {
	this.updatePreview();
    }

    private void italicCkActionPerformed(ActionEvent evt) {
	this.updatePreview();
    }

    private void approveBtActionPerformed(ActionEvent evt) {
	this.selectedOption = FontChooser.APPROVE_OPTION;
	if(this.isShowColorChooser){
	    this.selectedColor = this.previewColor;
	}
	this.selectedFont = this.previewFont;
	this.dialog.setVisible(false);
    }

    private void cancelBtActionPerformed(ActionEvent evt) {
	this.selectedOption = FontChooser.CANCEL_OPTION;
	this.dialog.setVisible(false);
    }

    private void fontTextFieldActionPerformed(ActionEvent evt) {
	this.fontTextInputCompleted(false);

    }

    private void sizeTextActionPerformed(ActionEvent evt) {
	this.sizeValueChanged(true, false);

    }

    private void fontTextFieldFocusLost(FocusEvent evt) {
	this.fontTextInputCompleted(true);
    }

    private void sizeTextFocusLost(FocusEvent evt) {
	this.sizeValueChanged(true, true);
    }

    private void fontListViewValueChanged(ListSelectionEvent evt){
	if(this.fontListView.getSelectedIndex() != -1){
	    this.lastAvailableFontFamilyName = this.fontListView.getSelectedValue().toString();
	    if(!this.fontText.isFocusOwner()){
		this.fontText.setText(this.lastAvailableFontFamilyName);
	    }
	    this.updatePreview();
	}
    }

    private void sizeListViewValueChanged(ListSelectionEvent evt){
	if(this.sizeListView.getSelectedIndex() != -1){
	    this.sizeValueChanged(false, true);
	}

    }

    private void previewTextMouseClicked(MouseEvent evt){
	if(evt.getClickCount()==2){
	    String input = JOptionPane.showInputDialog(this,
		    FontChooser.text_input_tip,
		    FontChooser.label_input_dialog_title,
		    JOptionPane.QUESTION_MESSAGE);
	    if(input!=null&&!input.trim().equals("")){
		this.previewText = input;
		this.previewTextArea.setText(this.previewText);
	    }

	}
    }

    private void resetBtActionPerformed(ActionEvent e){
	this.reset();
    }

    private JDialog createDialog(Component parent){

	Frame frame = JOptionPane.getFrameForComponent(parent);

	JDialog dialog = new JDialog(frame,this.title,true);
	dialog.setLayout(new BorderLayout());
	dialog.add(this);
	dialog.pack();
	dialog.setLocationRelativeTo(parent);

	return dialog;
    }

    private FontSize createFontSize(Font font){
	String size = String.valueOf(font.getSize());
	return new FontSize(size,size);
    }

    private FontSize createFontSize(String sizeName){
	FontSize size = this.preparedFontSizeMap.get(sizeName);
	if(size!=null){
	    return size;
	}else if(!StringUtils.isNaN(sizeName)){
	    return new FontSize(sizeName,sizeName);
	}

	return null;
    }


    private void fontTextInputCompleted(boolean isLoseFocus){

	if(!isLoseFocus){
	    this.fontListView.requestFocus();
	}

	String text = this.fontText.getText().trim();

	String re = this.availableFontFamilyNameMap.get(text.toLowerCase());
	if(re == null){
	    this.fontText.setText(this.lastAvailableFontFamilyName);
	    return;
	}

	this.lastAvailableFontFamilyName = re;
	this.fontText.setText(this.lastAvailableFontFamilyName);
	this.fontListView.setSelectedValue(this.lastAvailableFontFamilyName, true);

    }

    private void reset(){
	if(this.getSelectedFont() != null){
	    this.setSelectedFont(this.getSelectedFont());
	}else{
	    Font font = this.getSelectedFont();
	    this.setSelectedFont(FontChooser.INIT_FONT);
	    this.selectedFont = font;
	}

	if(this.getSelectedColor() !=null){
	    this.setSelectedColor(this.getSelectedColor());
	}else{
	    Color color = this.getSelectedColor();
	    this.setSelectedColor(FontChooser.INIT_COLOR);
	    this.selectedColor = color;
	}
    }

    private void sizeValueChanged(boolean isInput, boolean isTextFieldLoseFocus){
	String text = this.sizeText.getText().trim();

	if(isInput){
	    //verify it is a prepared size or not.
	    FontSize size = this.preparedFontSizeMap.get(text);
	    if(size != null){
		this.sizeListView.setSelectedValue(size, true);
		this.lastAvailableFontSize = size;
		this.updatePreview();
		return;
	    }

	    //create a new size
	    size = this.createFontSize(text);
	    if(size == null){
		this.sizeText.setText(this.lastAvailableFontSize.name);
		return;
	    }
	    this.lastAvailableFontSize = size;
	    this.sizeListView.clearSelection();
	    this.updatePreview();

	    return;
	}

	this.lastAvailableFontSize = (FontSize) this.sizeListView.getSelectedValue();
	this.sizeText.setText(this.lastAvailableFontSize.name);
	this.updatePreview();

	if(!isTextFieldLoseFocus){
	    this.sizeListView.requestFocus();
	}
    }


    /*
     * 更新预览文本
     */
    private void updatePreview(){
	String familyName = this.lastAvailableFontFamilyName.toString();
	float size = Float.parseFloat(this.lastAvailableFontSize.size);
	int style = Font.PLAIN;

	style += this.boldCk.isSelected() ? Font.BOLD : Font.PLAIN;
	style += this.italicCk.isSelected() ? Font.ITALIC : Font.PLAIN;

	this.previewFont = ViewUtils.createNewFont(familyName,style,(int) size);
	this.previewFont = this.previewFont.deriveFont(size);

	this.previewTextArea.setFont(this.previewFont);
	this.previewTextArea.setForeground(this.previewColor);
    }

    /*
     * 更新颜色选择器标记并更新预览文本。
     */
    private void colorChanged(Color newColor){
	if(newColor == null){
	    return;
	}
	this.previewColor = newColor;
	this.colorChooserLb.setIcon(ColoredPiece.create3DRect(newColor, 32, 16));
	this.updatePreview();
    }

    private boolean isAvailabelFontFamily(Font font){
	return this.isAvaliableFontFamily(font.getFamily());
    }

    private boolean isAvaliableFontFamily(String fontFamilyName){
	if(fontFamilyName == null){
	    return false;
	}
	String font = this.availableFontFamilyNameMap.get(fontFamilyName.toLowerCase());
	if(font == null){
	    return false;
	}
	return true;
    }



    //支持的字体映射表。键为纯小写的字体家族名,值为可识别的字体家族名。
    private final Map<String, String> availableFontFamilyNameMap = new HashMap<String, String>();

    //预定义的通用字号数组
    private final FontSize[] preparedFontSizeArray = new FontSize[]{new FontSize("8", "8"),
	    new FontSize("9", "9"), new FontSize("10", "10"),
	    new FontSize("11", "11"), new FontSize("12", "12"),
	    new FontSize("13", "13"), new FontSize("14", "14"),
	    new FontSize("16", "16"), new FontSize("18", "18"),
	    new FontSize("20", "20"), new FontSize("22", "22"),
	    new FontSize("24", "24"), new FontSize("26", "26"),
	    new FontSize("30", "30"), new FontSize("36", "36"),
	    new FontSize("48", "48")};
    //中文专用的字号表
    private final FontSize[] chineseFontSizeArray = new FontSize[]{
	    new FontSize("42", "\u521D\u53F7"),  
	    new FontSize("36", "\u5C0F\u521D"),new FontSize("26", "\u4E00\u53F7"),  
	    new FontSize("24", "\u5C0F\u4E00"), new FontSize("22", "\u4E8C\u53F7"),  
	    new FontSize("18", "\u5C0F\u4E8C"), new FontSize("16", "\u4E09\u53F7"), 
	    new FontSize("15", "\u5C0F\u4E09"), new FontSize("14", "\u56DB\u53F7"),  
	    new FontSize("12", "\u5C0F\u56DB"), new FontSize("10.5", "\u4E94\u53F7"),  
	    new FontSize("9", "\u5C0F\u4E94"), new FontSize("7.5", "\u516D\u53F7"), 
	    new FontSize("6.5", "\u5C0F\u516D")};  
    private List<FontSize> preparedFontSizeList = new ArrayList<FontSize>();
    private Map<String,FontSize> preparedFontSizeMap = new HashMap<String,FontSize>();

    private FontSize lastAvailableFontSize = null;

    private Font selectedFont = null;
    private Font previewFont = null;

    private Color selectedColor = null;
    private Color previewColor = null;

    private int selectedOption = 0;

    private boolean isShowColorChooser = false;
    private boolean isShowResetButton = false;

    private String title = FontChooser.label_dialog_title;
    private String lastAvailableFontFamilyName = null;
    private String previewText = FontChooser.text_preview_text;

    // Swing Components
    private JDialog dialog;
    private JButton approveBt;
    private JCheckBox boldCk;
    private JPanel buttonPanel;
    private JButton cancelBt;
    private JButton resetBt;
    private JPanel chooserPanel;
    private JLabel colorChooserLb;
    private JPanel colorChooserPanel;
    private JLabel colorLb;
    private JScrollPane fontListScrollPane;
    private JList fontListView;
    private JPanel fontPanel;
    private JTextField fontText;
    private JCheckBox italicCk;
    private JPanel previewPanel;
    private JTextArea previewTextArea;
    private JScrollPane sizeListScrollPane;
    private JList sizeListView;
    private JPanel sizePanel;
    private JTextField sizeText;
    private JPanel styleChooserPanel;
    private JPanel stylePanel;

    //Resources
    private static final String text_preview_text = "The quick brown fox jump over the lazy dog.\n";
    private static final String text_input_tip = "Input new text to preview.";

    private static final String label_dialog_title = "Choose a font";
    private static final String label_font_panel_title = "Font";
    private static final String label_size_panel_title = "Size";
    private static final String label_style_panel_title = "Style";
    private static final String label_bold_checkbox = "Bold";
    private static final String label_italic_checkbox = "Italic";
    private static final String label_color_chooser = "Color";
    private static final String label_color_chooser_title = "Choose a color";
    private static final String label_input_dialog_title = "Input";
    private static final String label_preview_panel_title = "Preview";
    private static final String label_approve_button = "Approve";
    private static final String label_cancel_button = "Cancel";
    private static final String label_reset_button = "Reset";

    private static final String tooltip_font_text = "Type a font name.";
    private static final String tooltip_font_list = "Select a font.";
    private static final String tooltip_size_text = "Type a font size.";
    private static final String tooltip_size_list = "Select a font size.";
    private static final String tooltip_color_chooser = "Click to choose a color.";
    private static final String tooltip_preview_text = "Double click to change a new text.";
    private static final String tooltip_approve_button = "Click to confirm";
    private static final String tooltip_cancel_button = "Click to cancel";
    private static final String tooltip_reset_button = "Click to reset settings";

}
